package com.kexilo.core.common.security;

import com.kexilo.core.common.annotation.Encrypted;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 字段加密工具类
 * 
 * @author Kexilo
 */
@Component
public class FieldEncryption {
    
    private static final Logger log = LoggerFactory.getLogger(FieldEncryption.class);
    
    @Value("${encryption.secret-key:kexilo-default-secret-key-2024}")
    private String secretKey;
    
    @Value("${encryption.rsa.public-key:}")
    private String rsaPublicKey;
    
    @Value("${encryption.rsa.private-key:}")
    private String rsaPrivateKey;
    
    @Value("${encryption.des.key:kexilo24}")
    private String desKey;
    
    private static final String AES_ALGORITHM = "AES";
    private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final String RSA_ALGORITHM = "RSA";
    private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
    private static final String DES_ALGORITHM = "DES";
    private static final String DES_TRANSFORMATION = "DES/ECB/PKCS5Padding";
    
    private KeyPair rsaKeyPair;
    
    /**
     * 加密字段
     * 
     * @param value     原始值
     * @param encrypted 加密注解
     * @return 加密后的值
     */
    public String encrypt(String value, Encrypted encrypted) {
        if (!StringUtils.hasText(value)) {
            return value;
        }
        
        try {
            switch (encrypted.algorithm()) {
                case AES:
                    return encryptWithAES(value);
                case RSA:
                    return encryptWithRSA(value);
                case DES:
                    return encryptWithDES(value);
                default:
                    return encryptWithAES(value);
            }
        } catch (Exception e) {
            log.error("字段加密失败", e);
            return value;
        }
    }
    
    /**
     * 解密字段
     * 
     * @param encryptedValue 加密值
     * @param encrypted      加密注解
     * @return 解密后的值
     */
    public String decrypt(String encryptedValue, Encrypted encrypted) {
        if (!StringUtils.hasText(encryptedValue)) {
            return encryptedValue;
        }
        
        try {
            switch (encrypted.algorithm()) {
                case AES:
                    return decryptWithAES(encryptedValue);
                case RSA:
                    return decryptWithRSA(encryptedValue);
                case DES:
                    return decryptWithDES(encryptedValue);
                default:
                    return decryptWithAES(encryptedValue);
            }
        } catch (Exception e) {
            log.error("字段解密失败", e);
            return encryptedValue;
        }
    }
    
    /**
     * 脱敏显示
     * 
     * @param value     原始值
     * @param encrypted 加密注解
     * @return 脱敏后的值
     */
    public String mask(String value, Encrypted encrypted) {
        if (!StringUtils.hasText(value) || !encrypted.maskOnOutput()) {
            return value;
        }
        
        int length = value.length();
        int keepPrefix = Math.min(encrypted.keepPrefix(), length);
        int keepSuffix = Math.min(encrypted.keepSuffix(), length);
        
        if (keepPrefix + keepSuffix >= length) {
            return value;
        }
        
        StringBuilder masked = new StringBuilder();
        
        // 保留前缀
        if (keepPrefix > 0) {
            masked.append(value.substring(0, keepPrefix));
        }
        
        // 脱敏中间部分
        int maskLength = length - keepPrefix - keepSuffix;
        for (int i = 0; i < maskLength; i++) {
            masked.append(encrypted.maskChar());
        }
        
        // 保留后缀
        if (keepSuffix > 0) {
            masked.append(value.substring(length - keepSuffix));
        }
        
        return masked.toString();
    }
    
    /**
     * AES加密
     */
    private String encryptWithAES(String plainText) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(getAESKey(), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        
        byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }
    
    /**
     * AES解密
     */
    private String decryptWithAES(String encryptedText) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(getAESKey(), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        
        byte[] encrypted = Base64.getDecoder().decode(encryptedText);
        byte[] decrypted = cipher.doFinal(encrypted);
        return new String(decrypted, StandardCharsets.UTF_8);
    }
    
    /**
     * 获取AES密钥
     */
    private byte[] getAESKey() {
        // 确保密钥长度为16字节（128位）
        String key = secretKey;
        if (key.length() > 16) {
            key = key.substring(0, 16);
        } else if (key.length() < 16) {
            key = String.format("%-16s", key).replace(' ', '0');
        }
        return key.getBytes(StandardCharsets.UTF_8);
    }
    
    /**
     * 生成AES密钥
     */
    public static String generateAESKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
            keyGenerator.init(128);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (Exception e) {
            log.error("生成AES密钥失败", e);
            return null;
        }
    }
    
    /**
     * RSA加密
     */
    private String encryptWithRSA(String value) {
        try {
            PublicKey publicKey = getRSAPublicKey();
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            
            byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
            
        } catch (Exception e) {
            log.error("RSA加密失败", e);
            throw new RuntimeException("RSA加密失败: " + e.getMessage());
        }
    }
    
    /**
     * RSA解密
     */
    private String decryptWithRSA(String encryptedValue) {
        try {
            PrivateKey privateKey = getRSAPrivateKey();
            Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            
            byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
            return new String(decrypted, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("RSA解密失败", e);
            throw new RuntimeException("RSA解密失败: " + e.getMessage());
        }
    }
    
    /**
     * DES加密
     */
    private String encryptWithDES(String value) {
        try {
            SecretKey secretKey = getDESKey();
            Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
            
        } catch (Exception e) {
            log.error("DES加密失败", e);
            throw new RuntimeException("DES加密失败: " + e.getMessage());
        }
    }
    
    /**
     * DES解密
     */
    private String decryptWithDES(String encryptedValue) {
        try {
            SecretKey secretKey = getDESKey();
            Cipher cipher = Cipher.getInstance(DES_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
            return new String(decrypted, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("DES解密失败", e);
            throw new RuntimeException("DES解密失败: " + e.getMessage());
        }
    }
    
    /**
     * 获取RSA公钥
     */
    private PublicKey getRSAPublicKey() {
        try {
            if (StringUtils.hasText(rsaPublicKey)) {
                byte[] keyBytes = Base64.getDecoder().decode(rsaPublicKey);
                X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
                return keyFactory.generatePublic(spec);
            } else {
                return getRSAKeyPair().getPublic();
            }
        } catch (Exception e) {
            throw new RuntimeException("获取RSA公钥失败", e);
        }
    }
    
    /**
     * 获取RSA私钥
     */
    private PrivateKey getRSAPrivateKey() {
        try {
            if (StringUtils.hasText(rsaPrivateKey)) {
                byte[] keyBytes = Base64.getDecoder().decode(rsaPrivateKey);
                PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
                return keyFactory.generatePrivate(spec);
            } else {
                return getRSAKeyPair().getPrivate();
            }
        } catch (Exception e) {
            throw new RuntimeException("获取RSA私钥失败", e);
        }
    }
    
    /**
     * 获取RSA密钥对
     */
    private synchronized KeyPair getRSAKeyPair() {
        if (rsaKeyPair == null) {
            try {
                KeyPairGenerator keyGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
                keyGen.initialize(2048);
                rsaKeyPair = keyGen.generateKeyPair();
                log.warn("使用临时生成的RSA密钥对，建议在配置文件中设置固定密钥");
            } catch (Exception e) {
                throw new RuntimeException("生成RSA密钥对失败", e);
            }
        }
        return rsaKeyPair;
    }
    
    /**
     * 获取DES密钥
     */
    private SecretKey getDESKey() {
        try {
            String key = desKey;
            if (key.length() > 8) {
                key = key.substring(0, 8);
            } else if (key.length() < 8) {
                key = String.format("%-8s", key).replace(' ', '0');
            }
            
            DESKeySpec keySpec = new DESKeySpec(key.getBytes(StandardCharsets.UTF_8));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
            return keyFactory.generateSecret(keySpec);
            
        } catch (Exception e) {
            throw new RuntimeException("生成DES密钥失败", e);
        }
    }
}
